/**
 *
 * @author John Creecy
 */
package battlebarnserver;

import battlebarnapplet.ClientStart;
import battlebarnserver.entities.BaseEntity;
import battlebarnserver.entities.EventEntity;
import battlebarnserver.entities.MovementEntity;
import battlebarnserver.entities.PlayerEntity;
import battlebarnserver.entities.ProjectileEntity;
import battlebarnserver.util.Constants;
import battlebarnserver.util.Conversions;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class StateGenerator implements Runnable {

	public void run() {
		calculateState();
	}

	private void calculateState() {
		PlayerEntity playerEntity;
		Iterator it = BattleBarn.actionStack.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry mapEntry = (Map.Entry) it.next();
			playerEntity = (PlayerEntity) mapEntry.getValue();
			BattleBarn.gameState.add(playerEntity);
		}

		moveAI();
		moveProjectiles();
		removeDeadPlayers();
		winnerTest();
		activityTracker();
	}
	
	private void moveAI() {
		PlayerEntity playerEntity;
		BattleBarn.enemyMovement = 0;
		int facing;
		Iterator it = BattleBarn.actionStack.entrySet().iterator();
		while (it.hasNext()) {
			Map.Entry mapEntry = (Map.Entry) it.next();
			playerEntity = (PlayerEntity) mapEntry.getValue();
			if(playerEntity.getPlayerId().substring(0,1).equals("A") && playerEntity.isPlayerDead() == false) {
				facing = playerEntity.getFacing();
				MovementEntity movementEntity = Conversions.generateRandomMovement(playerEntity.getTileX(), playerEntity.getTileY(), facing);
				if(Conversions.collisionTest(movementEntity.getTileX(), movementEntity.getTileY())) {
					PlayerEntity enemy = new PlayerEntity(playerEntity.getPlayerId(), playerEntity.getHealth(),movementEntity.getTileX(), movementEntity.getTileY(), facing, false, playerEntity.getLastActionMove());	
					BattleBarn.actionStack.put(playerEntity.getPlayerId(),enemy);
					BattleBarn.enemyMovement++;
				}
			}
		}
	}

	private void moveProjectiles() {
		int tileX;
		int tileY;
		int velocityX;
		int velocityY;
		String projectileId;
		HashMap<Integer, String> shotXLocs = new HashMap<Integer, String>();
		HashMap<Integer, String> shotYLocs = new HashMap<Integer, String>();
		ProjectileEntity projectileEntity;
		for (int i = 0; i < BattleBarn.activeProjectiles.size(); i++) {
			projectileEntity = BattleBarn.activeProjectiles.get(i);
			if(BattleBarn.removeProjectiles.contains(projectileEntity.getProjectileId())) {
				BattleBarn.activeProjectiles.remove(i);
			} else {
				velocityX = projectileEntity.getVelocityX();
				velocityY = projectileEntity.getVelocityY();
				tileX = projectileEntity.getTileX() + projectileEntity.getVelocityX();
				tileY = projectileEntity.getTileY() + projectileEntity.getVelocityY();
				shotXLocs.put(tileX, projectileEntity.getProjectileId());
				shotYLocs.put(tileY, projectileEntity.getProjectileId());
				projectileId = projectileEntity.getProjectileId();
	
				BattleBarn.activeProjectiles.remove(i);
				if (Conversions.testBoundsX(tileX) && Conversions.testBoundsY(tileY)) {
					projectileEntity = new ProjectileEntity(projectileId, velocityX, velocityY, tileX, tileY);
					BattleBarn.activeProjectiles.add(projectileEntity);
				}
			}
		}

		if (shotXLocs.size() > 0) {
			calculateDamage(shotXLocs, shotYLocs);
		}

	}

	private void calculateDamage(HashMap<Integer, String> shotXLocs, HashMap<Integer, String> shotYLocs) {
		PlayerEntity newPlayerEntity;
		PlayerEntity oldBaseEntity;
		for (int i = 0; i < BattleBarn.gameState.size(); i++) {
			if (BattleBarn.gameState.get(i).getType().equals("Player")) {
				oldBaseEntity = (PlayerEntity) BattleBarn.gameState.get(i);
				if (shotXLocs.containsKey(oldBaseEntity.getTileX()) && shotYLocs.containsKey(oldBaseEntity.getTileY())) {
					 newPlayerEntity = new PlayerEntity(oldBaseEntity.getPlayerId(), (oldBaseEntity.getHealth()-Constants.PROJECTILE_DAMAGE), oldBaseEntity.getTileX(), oldBaseEntity.getTileY(), oldBaseEntity.getFacing(),false, oldBaseEntity.getLastActionMove());
					 BattleBarn.actionStack.put(oldBaseEntity.getPlayerId(), newPlayerEntity);
					 EventEntity eventEntity = new EventEntity(oldBaseEntity.getTileX(), oldBaseEntity.getTileY(), 1, shotXLocs.get(oldBaseEntity.getTileX()));
					 BattleBarn.gameState.add(eventEntity);
					 BattleBarn.removeProjectiles.add(shotXLocs.get(oldBaseEntity.getTileX()));
				}
			}
		}
	}
	
	private void removeDeadPlayers() {
		PlayerEntity oldPlayerEntity;
		PlayerEntity newPlayerEntity;
		for (int i = 0; i < BattleBarn.gameState.size(); i++) {
			if (BattleBarn.gameState.get(i).getType().equals("Player")) {
				oldPlayerEntity = (PlayerEntity) BattleBarn.gameState.get(i);
				if(oldPlayerEntity.getHealth() <= Constants.MIN_PLAYER_HEALTH && oldPlayerEntity.isPlayerDead() == false) {
					 // TODO: use events to remove player from client/server
					 newPlayerEntity = new PlayerEntity(oldPlayerEntity.getPlayerId(), Constants.MIN_PLAYER_HEALTH, Constants.TILE_MAX_X+1, Constants.TILE_MAX_Y+1, oldPlayerEntity.getFacing(), true, oldPlayerEntity.getLastActionMove());
					 BattleBarn.actionStack.put(oldPlayerEntity.getPlayerId(), newPlayerEntity);
					 BattleBarn.deaths++;
				}
			}
		}
	}
	
	private void winnerTest() {
		PlayerEntity playerEntity;
		String winningPlayer = "";
		if(BattleBarn.deaths == (BattleBarn.actionStack.size()-1)) {
			Iterator it = BattleBarn.actionStack.entrySet().iterator();
			while (it.hasNext()) {
				Map.Entry mapEntry = (Map.Entry) it.next();
				playerEntity = (PlayerEntity) mapEntry.getValue();
				if(playerEntity.isPlayerDead() == false) {
					 EventEntity eventEntity = new EventEntity(0,0, 2, playerEntity.getPlayerId());
					 BattleBarn.gameState.add(eventEntity);
				}
			}
		}
	}

	private void activityTracker() {

		if (BattleBarn.packetsIn == BattleBarn.packetsInSnapshot && BattleBarn.activeProjectiles.size() == 0 && BattleBarn.enemyMovement == 0) {
			// no player activity this cycle
			BattleBarn.gameState.clear();
		} else {
			sendState();
		}

		// stats
		BattleBarn.packetsInSnapshot = BattleBarn.packetsIn;
		if (BattleBarn.actionStack.size() > BattleBarn.peakUsers) {
			BattleBarn.peakUsers = BattleBarn.actionStack.size();
		}
	}

	private void sendState() {
		DatagramPacket sendPacket;
		String outData;
		int directionId;
		String xPad;
		String yPad;
		PlayerEntity playerEntity;
		ProjectileEntity projectileEntity;
		EventEntity eventEntity;
		for (int i = 0; i < BattleBarn.gameState.size(); i++) {
			try {
				Long timeStamp = (long) (System.currentTimeMillis());
				byte[] gameData = new byte[1024];
				DatagramSocket clientSocket = new DatagramSocket();
				outData = "";

				BaseEntity baseEntity = (BaseEntity) BattleBarn.gameState.get(i);

				// player action
				if (baseEntity.getType().equals("Player")) {
					playerEntity = (PlayerEntity) BattleBarn.gameState.get(i);
					xPad = Conversions.padCoordinate(Integer.toString(Conversions.tileToPixel(playerEntity.getTileX())));
					yPad = Conversions.padCoordinate(Integer.toString(Conversions.tileToPixel(playerEntity.getTileY())));
					outData = timeStamp + xPad + yPad + playerEntity.getFacing() + Conversions.boolToInt(playerEntity.getLastActionMove()) + Conversions.padHealth(playerEntity.getHealth()) + playerEntity.getPlayerId();
				// new projectile
				} else if (baseEntity.getType().equals("Projectile")) {
					projectileEntity = (ProjectileEntity) BattleBarn.gameState.get(i);
					xPad = Conversions.padCoordinate(Integer.toString(Conversions.tileToPixel(projectileEntity.getTileX())));
					yPad = Conversions.padCoordinate(Integer.toString(Conversions.tileToPixel(projectileEntity.getTileY())));
					directionId = Conversions.getDirectionId(projectileEntity.getVelocityX(), projectileEntity.getVelocityY()) ;
					outData = timeStamp + xPad + yPad + "P" + directionId + projectileEntity.getProjectileId();
				// new event trigger
				} else if (baseEntity.getType().equals("Event")) {
					eventEntity = (EventEntity) BattleBarn.gameState.get(i);
					outData = timeStamp + "000000" + "E" + eventEntity.getEventId() + eventEntity.getPlayerId();
				}
				gameData = outData.getBytes();

				Iterator it = BattleBarn.clientList.entrySet().iterator();
				while (it.hasNext()) {
					Map.Entry mapEntry = (Map.Entry) it.next();
					InetAddress IPAddress = InetAddress.getByName((String) mapEntry.getKey());
					sendPacket = new DatagramPacket(gameData, gameData.length, IPAddress, ClientStart.clientPort);
					clientSocket.send(sendPacket);
					BattleBarn.packetsOut++;
				}
			} catch (IOException ex) {
				Logger.getLogger(StateGenerator.class.getName()).log(Level.SEVERE, null, ex);
			}
		}
		BattleBarn.gameState.clear();
	}
}
